﻿using System;
using System.Diagnostics.Contracts;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Threading.Tasks;
using Contracts.General;
using Wombat.Common;
using Wombat.Core.Gates;
using Wombat.Core.Services;

namespace Wombat.WcfGate
{
    public class WcfServiceGateHost: ServiceEndpointBaseGate
    {
        public delegate WcfServiceGateHost Factory();

        private ServiceHost _host;
        private const string DefaultBaseAddress = "localhost:1360";
        private string _endPointBaseAddress;


        public WcfServiceGateHost(IObservationManager observationManager, IConfigurationService configurationService, IDiagnosticsService diagnosticsService, WcfFloatTagService.Factory wcfFloatServiceGateFactory) 
           : base(observationManager, configurationService, diagnosticsService)
        {
            WcfFloatTagService.CheatingFactory = () => wcfFloatServiceGateFactory(this);
        }

        async public override Task StartAsync()
        {
            Contract.Assert(Configurations.ContainsKey(ConfigurationKeys.EndPointType));

            _endPointBaseAddress = (Configurations.ContainsKey(ConfigurationKeys.BaseEndPointAddress))
                                       ? (string)Configurations[ConfigurationKeys.BaseEndPointAddress].Value
                                       : DefaultBaseAddress;
            var endPoints = ((string)Configurations[ConfigurationKeys.EndPointType].Value).Split(new[] {','});
            foreach (var endPoint in endPoints)
            {
                AddEndPoint((EndPointType)Enum.Parse(typeof(EndPointType), endPoint));
            }
            if (_host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
            {
                var metadataBehaviour = new ServiceMetadataBehavior {HttpGetEnabled = true};
                _host.Description.Behaviors.Add(metadataBehaviour);
            }
            _host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
                                     MetadataExchangeBindings.CreateMexHttpBinding(), "mex");

            await StartHostAsync();
        }

        async private Task StartHostAsync()
        {
            try
            {
                _host.Faulted += (o, e) =>
                                     {
                                         DiagnosticsService.WriteFault("Hosting service faulted.");
                                         StopAsync().Wait();
                                     };
                var host = _host;
                await Task.Factory.FromAsync(host.BeginOpen, host.EndOpen, null);
            }
            catch (Exception exception)
            {
                DiagnosticsService.WriteFault(string.Format("Hosting service faulted :{0}.", exception));
                _host.NullSafe(h => h.Close());
            }
        }

        private void AddEndPoint(EndPointType endPointType)
        {
            _host = _host ??  new ServiceHost(typeof(WcfFloatTagService), new Uri(_endPointBaseAddress));

            var binding = default (Binding);
            var addressPattern = string.Empty;
            switch (endPointType)
            {
                case EndPointType.Basic:
                    binding = new BasicHttpBinding();
                    addressPattern = "http://{0}/{1}/{2}";
                    break;
                case EndPointType.NetHttp:
                    binding = new NetHttpBinding(BasicHttpSecurityMode.None);
                    break;
                case EndPointType.Rest:
                    binding = new WebHttpBinding(WebHttpSecurityMode.None);
                    addressPattern = "http://{0}/{1}/{2}";
                    break;
                case EndPointType.Tcp:
                    binding = new NetTcpBinding(SecurityMode.Transport);
                    addressPattern = "net.tcp://{0}/{1}/{2}";
                    break;
                case EndPointType.Ws:
                    binding = new WSHttpBinding(SecurityMode.None);
                    addressPattern = "net.http://{0}/{1}/{2}";
                    break;
                default:
                    throw new InvalidOperationException(string.Format("Binding '{0}' is not supported.",
                        string.Format(addressPattern, _endPointBaseAddress, endPointType)));
            }
            //_host.AddServiceEndpoint(typeof(ITagService), binding, string.Format(addressPattern, _endPointBaseAddress, "Tag", endPointType));
            _host.AddServiceEndpoint(typeof (IDataSubmissionService), binding, "DataSubmission");
            //string.Format(addressPattern, _endPointBaseAddress, "DataSubmission", endPointType));
        }

        async public override Task StopAsync()
        {
            try
            {
                if (_host.State == CommunicationState.Faulted)
                    _host.Abort();
                else
                    await Task.Factory.FromAsync( (r, state) => _host.BeginClose(r, state), r => _host.EndClose(r), null);
            }
            catch (Exception)
            {
                _host.Close();
            }
            finally
            {
                _host = null;
            }
        }

        protected override void DisposeService()
        {
            if (_host != null)
            {
                StopAsync().Wait();
            }
        }
    }
}